package com.cjie.ecommerce.service.impl;

import com.cjie.ecommerce.dao.ProductWithSimpleCacheRepository;
import com.cjie.ecommerce.entity.Category;
import com.cjie.cache.tool.JoinPointCacheKeyGadget;
import com.cjie.cache.aop.CjieSimpleCache;
import com.cjie.ecommerce.dao.CategoryRepository;
import com.cjie.ecommerce.entity.Product;
import com.cjie.ecommerce.service.ProductWithSimpleCacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ProductWithSimpleCacheServiceImpl implements ProductWithSimpleCacheService {
    private static final Logger log = LoggerFactory.getLogger(ProductWithSimpleCacheServiceImpl.class);
    private final CategoryRepository categoryRepository;
    private final ProductWithSimpleCacheRepository productWithSimpleCacheRepository;

    @Autowired
    public ProductWithSimpleCacheServiceImpl(CategoryRepository categoryRepository,
                                             ProductWithSimpleCacheRepository productWithSimpleCacheRepository) {
        this.categoryRepository = categoryRepository;
        this.productWithSimpleCacheRepository = productWithSimpleCacheRepository;
    }

    /**
     * 获取所有产品信息
     */
    @CjieSimpleCache(
            keySpelExpression = "ProductWithSimpleCacheServiceImpl.getAllProducts",
            type = CjieSimpleCache.CacheType.MULTI_LEVEL,
            expire = 120,
            cacheNull = false
    )
    @Override
    public List<Product> getAllProducts() {
        return productWithSimpleCacheRepository.findAll();
    }

    /**
     * 修改产品信息
     * @param product 新的产品信息
     */
    @Override
    public Product updateProduct(Product product) {
        if (product.getId() == null) {
            throw new IllegalArgumentException("Product ID cannot be null for update");
        }
        return productWithSimpleCacheRepository.save(product);
    }

    /**
     * 删除产品
     * @param id 产品编号
     */
    @Override
    public void deleteProduct(Long id) {
        productWithSimpleCacheRepository.deleteById(id);
    }

    /**
     * 创建产品信息
     * @param product 产品信息
     */
    @Override
    public Product createProduct(Product product) {
        if (product.getId() != null) {
            throw new IllegalArgumentException("New product ID must be null");
        }
        return productWithSimpleCacheRepository.save(product);
    }

    /**
     * 根据产品编号获取产品信息，带多级缓存。
     * 使用多级缓存，解决三大问题
     * 1. 防穿透：cacheNull=true
     * 2. 防击穿：lockTimeout=300ms
     * 3. 防雪崩：随机过期时间
     * @param id 产品编号
     */
    @CjieSimpleCache(
            //用来生成缓存键的SpEL表达式，注意：:符号会被解析器认为是非法字符，所以要写在单引号内
            keySpelExpression = "'product:' + #id",
            type = CjieSimpleCache.CacheType.MULTI_LEVEL,
            expire = 300,
            cacheNull = true,
            nullExpire = 60,
            lockExpireTime = 300,
            local = @CjieSimpleCache.LocalConfig(
                    expire = 120
            )
    )
    @Override
    public Product getProductByIdWithMultiCache(Long id) {
        // 数据库查询逻辑
        return productWithSimpleCacheRepository.findById(id);
    }

    /**
     * 根据产品编号获取产品信息，带多级缓存，缓存键使用OGNL表达式生成。
     *
     * @param id 产品编号
     */
    @CjieSimpleCache(
            keyOgnlExpression = "ProductWithSimpleCacheServiceImpl.getProductByIdWithMultiCacheOgnl.{id}",//用来生成缓存键的OGNL表达式
            type = CjieSimpleCache.CacheType.MULTI_LEVEL,//缓存类型：多级缓存
            expire = 300, //分布式缓存过期时间
            cacheNull = true, //是否缓存null值(防穿透)
            nullExpire = 60, //空值过期时间(秒，仅当cacheNull=true时有效)
            lockExpireTime = 300, //互斥锁超时时间(毫秒，防击穿)
            local = @CjieSimpleCache.LocalConfig(//本地缓存配置
                    expire = 120
            )
    )
    @Override
    public Product getProductByIdWithMultiCacheOgnl(Long id) {
        // 数据库查询逻辑
        return productWithSimpleCacheRepository.findById(id);
    }

    /**
     * 根据产品编号获取产品信息，带本地缓存
     * @param id 产品编号
     */
    @CjieSimpleCache(
            keyOgnlExpression = "ProductWithSimpleCacheServiceImpl.getProductByIdWithLocalCache.{id}",
            type = CjieSimpleCache.CacheType.LOCAL,
            expire = 300,
            cacheNull = true,
            nullExpire = 60,
            lockExpireTime = 300,
            local = @CjieSimpleCache.LocalConfig(
                    expire = 120
            )
    )
    @Override
    public Product getProductByIdWithLocalCache(Long id) {
        // 数据库查询逻辑
        return productWithSimpleCacheRepository.findById(id);
    }

    /**
     * 获取指定分类下的精选产品，带本地缓存
     *
     * @param category 类别对象【仅用于演示相关缓存注解的用法，实际项目中是不会这么传数据的】
     * @return
     */
    @CjieSimpleCache(
            keyGadget = JoinPointCacheKeyGadget.CACHE_KEY_GADGET_CATEGORY,
            type = CjieSimpleCache.CacheType.LOCAL,
            expire = 300,
            cacheNull = true,
            nullExpire = 60,
            lockExpireTime = 300,
            local = @CjieSimpleCache.LocalConfig(
                    expire = 120
            )
    )
    @Override
    public List<Product> getSelectedProductsByCategory(Category category) {
        // 模拟数据库查询逻辑
        return productWithSimpleCacheRepository.getSelectedProductsByCategoryId(category.getId());
    }

    /**
     * 获取热门产品列表。
     * 仅使用分布式缓存
     */
    @CjieSimpleCache(
            keySpelExpression = "hot_products",
            type = CjieSimpleCache.CacheType.DISTRIBUTED,
            expire = 300,
            cacheNull = false
    )
    @Override
    public List<Product> getHotProducts() {
        // 数据库查询逻辑
        return productWithSimpleCacheRepository.findByHot(true);
    }

    /**
     * 获取所有的分类信息。
     * 仅使用本地缓存
     */
    @CjieSimpleCache(
            keySpelExpression = "categories",
            type = CjieSimpleCache.CacheType.LOCAL,
            local = @CjieSimpleCache.LocalConfig(
                    expire = 90
            )
    )
    @Override
    public List<Category> getAllCategories() {
        // 数据库查询逻辑
        return categoryRepository.findAll();
    }

    /**
     * 获取产品的详细信息，带多级缓存，使用指定方法生成缓存键
     * @param productId 产品编号
     */
    @CjieSimpleCache(
            keyGenerator = "generateProductDetailCacheKey",
            type = CjieSimpleCache.CacheType.MULTI_LEVEL,
            expire = 300,
            cacheNull = true,
            nullExpire = 60,
            lockExpireTime = 300,
            local = @CjieSimpleCache.LocalConfig(
                    expire = 120
            )
    )
    @Override
    public Map<String, Object> getProductDetail(Long productId) {
        // 模拟数据库查询
        return new HashMap<String, Object>() {{
            put("id", productId);
            put("name", "商品详情");
            put("price", 99.9);
            put("stock", 100);
        }};
    }

    /**
     * 产品详细信息的缓存键生成方法(缓存切面中会通过反射调用此方法)
     */
    public String generateProductDetailCacheKey(Long productId) {
        return "product:detail:" + productId;
    }
}
